/**
 * This is the SGGWM class. It is the window manager for the SGG.
 */
package edu.gatech.spacetraderteam3.view;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import edu.gatech.spacetraderteam3.debug.Debug;
import edu.gatech.spacetraderteam3.model.GameState;

/**
 * StandardGalacticGUIWindowManager. A static class to handle events for the custom GUI.
 * 
 * The successor to ViewMasterFunkyFred, may he rest in peace.
 * 
 * @author Alexander Ikonomidis
 * @version 1.0
 */

public class SGGWM { // Standard Galactic GUI Window Manager.
    /**
     * The map of window groups to groups of input delegates.
     */
    private static Map<GameState, ArrayList<SGGWindow.InputDelegate>> WindowGroups = null;

    /**
     * The container to receive events from and to request repaints of.
     */
    private static Container Container = null;

    /**
     * The focused window delegate.
     */
    private static SGGWindow.InputDelegate FocusedWindowDelegate = null;

    /**
     * The active window groups to send events to.
     */
    private static GameState[] ActiveWindowGroups = new GameState[] { GameState.DEFAULT };

    /**
     * Registers a container as the containter to listen for events from.
     * 
     * @param container
     *            the container to be registered
     */
    public static void registerContainer(Container container) {
        SGGWM.Container = container;
        container.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent k) {
                if (FocusedWindowDelegate != null) {
                    FocusedWindowDelegate.doKey(k);
                }
                Debug.write("Key Click: " + k.getKeyChar(), Debug.VERBOSE);
            }
        });
        final MouseAdapter mouseDelegate = new MouseDelegate();
        container.addMouseListener(mouseDelegate);
        container.addMouseMotionListener(mouseDelegate);
    }

    /**
     * Sets the currently listening window group.
     * 
     * @param windowGroup
     *            the window group to recieve events
     */
    public static void setWindowGroup(GameState... windowGroup) {
        SGGWM.ActiveWindowGroups = windowGroup;
        FocusedWindowDelegate = null;
    }

    /**
     * Registers a window to be eligible to recieve mouse/keyboard events.
     * 
     * @param sggc
     *            the listener to register
     * @param windowGroup
     *            the window group to assign it to
     */
    protected static void registerSGGWindow(SGGWindow.InputDelegate sggc,
            GameState windowGroup) {
        if (WindowGroups == null) {
            WindowGroups = new HashMap<GameState, ArrayList<SGGWindow.InputDelegate>>();
            for (GameState wG : GameState.values())
                WindowGroups.put(wG, new ArrayList<SGGWindow.InputDelegate>());
        }
        WindowGroups.get(windowGroup).add(sggc);
    }

    /**
     * Changes what window group a window should be registered with.
     * 
     * @param sggc
     *            the listener to change
     * @param windowGroup
     *            the new window group
     */
    public static void changeRegistration(SGGWindow.InputDelegate sggc,
            GameState windowGroup) {
        if (WindowGroups == null) {
            WindowGroups = new HashMap<GameState, ArrayList<SGGWindow.InputDelegate>>();
            for (GameState wG : GameState.values())
                WindowGroups.put(wG, new ArrayList<SGGWindow.InputDelegate>());
        }
        WindowGroups.get(sggc.getWindowGroup()).remove(sggc);
        WindowGroups.get(windowGroup).add(sggc);
    }

    /**
     * Removes a window from its listener group so that it will stop receiving events.
     * 
     * @param sggc
     *            the listener to dispose of
     */
    protected static void disposeSGGWindow(SGGWindow.InputDelegate sggc) {
        WindowGroups.get(sggc.getWindowGroup()).remove(sggc);
    }

    /**
     * Returns the dimensions of the window.
     * 
     * @return the dimensions of the window
     */
    protected static Dimension getWindowSize() {
        if (Container == null) {
            return new Dimension(800, 600);
        } else {
            return Container.getSize();
        }
    }

    /**
     * Requests that the Container repaints itself.
     */
    public static void requestRepaint() {// TODO: make this use the rectangle repaint
                                         // method without tearing
        if (Container != null) {
            Container.repaint();
        }
    }

    /**
     * Sets the focused window. This is a utility method for this class.
     * 
     * @param sggw
     *            the window to receive focus
     */
    private static void setFocusedWindow(SGGWindow.InputDelegate sggw) {
        FocusedWindowDelegate = sggw;
    }

    /**
     * The mouse delegate the Container will send events to.
     * 
     * @author Alexander Ikonomidis
     * 
     */
    private static class MouseDelegate extends MouseAdapter {
        /**
         * Receives mouse events.
         * 
         * @param m
         *            the mouse event to process
         */
        public void mousePressed(MouseEvent m) {
            Debug.write("Mouse Click: " + m.getLocationOnScreen().getX() + " "
                    + m.getLocationOnScreen().getY(), Debug.VERBOSE);
            if (WindowGroups == null) {
                return;
            }
            for (GameState group : ActiveWindowGroups) {
                for (SGGWindow.InputDelegate sggw : WindowGroups.get(group)) {
                    if (sggw.contains(m.getPoint())) {
                        sggw.doClick(m);
                        if (!(FocusedWindowDelegate == null || FocusedWindowDelegate
                                .equals(sggw))) {
                            FocusedWindowDelegate.loseFocus(m.getPoint()); // $codepro.audit.disable
                                                                           // nullPointerDereference
                        }
                        setFocusedWindow(sggw);
                        return;
                    }
                }
            }
            if (FocusedWindowDelegate != null) {
                FocusedWindowDelegate.loseFocus(m.getPoint());
            }
        }

        /**
         * Receives mouse released events.
         * 
         * @param m
         *            the release event to process
         */
        public void mouseReleased(MouseEvent m) {
            Debug.write("Mouse Release: " + m.getLocationOnScreen().getX() + " "
                    + m.getLocationOnScreen().getY(), Debug.VERBOSE);
            if (FocusedWindowDelegate != null) {
                FocusedWindowDelegate.doRelease();
            }
            Debug.write("\n", Debug.VERBOSE);
        }

        /**
         * Receives mouse dragged events.
         * 
         * @param m
         *            the drag event to process
         */
        public void mouseDragged(MouseEvent m) {
            if (FocusedWindowDelegate != null) {
                FocusedWindowDelegate.doDrag(m);
            }
        }

        /**
         * The toString method.
         * 
         * @return "A MouseDelegate object."
         */
        public String toString() {
            return "A MouseDelegate object.";
        }
    }

    /**
     * The toString method.
     * 
     * @return WindowGroups.toString()
     */
    public String toString() {
        return WindowGroups.toString();
    }
}
